/*
 * SecuentialMerge.h
 *
 *  Created on: Nov 3, 2013
 *      Author: SG0214746
 */

#ifndef SECUENTIALMERGE_H_
#define SECUENTIALMERGE_H_

#include "SecuentialFile.h"
#include "MergeEntry.h"
#include "../../utils/Utils.h"

#define MERGE_EXT ".mrg"
#define MERGE_NAME "merge"

template <class T>
class SecuentialMerge {
	private:
		typedef std::vector<MergeEntry<T>* > MergeVector;

		MergeVector mergeVector;
		StrVector temps;
		BigInt tempFilesBlockSize;
		int mergeLines;
		String mergeName;

		BigInt loadMergeVector();
		void initNewTempFile(SecuentialFile<T>& file);
		void initMergeVector();

	public:
		SecuentialMerge(StrVector& files, BigInt tempFilesBlockSize, int mergeLines);
		virtual ~SecuentialMerge();

		void setMergeName(const String& name);
		String merge(Compare comparator);
};

template <class T>
SecuentialMerge<T>::SecuentialMerge(StrVector& files, BigInt tempFilesBlockSize, int mergeLines) : temps(files) {
	this->tempFilesBlockSize = tempFilesBlockSize;
	this->mergeLines = mergeLines;
	for (int x = 0; x < mergeLines; x++) {
		mergeVector.push_back(new MergeEntry<T>(tempFilesBlockSize));
	}
	mergeName = MERGE_NAME;
}

template <class T>
SecuentialMerge<T>::~SecuentialMerge() {
	for (BigInt x = 0; x < mergeVector.size(); x++) {
		delete mergeVector[x];
	}
}

template <class T>
void SecuentialMerge<T>::setMergeName(const String& name) {
	mergeName = name;
}

template <class T>
String SecuentialMerge<T>::merge(Compare comparator) {
	BigInt smallestIndex;
	T* smallest;
	T* aux;
	if (temps.size() > 1) {
		SecuentialFile<T> merged(tempFilesBlockSize);
		int filesInRound;
		while (temps.size() > 1) {
			filesInRound = loadMergeVector();
			initNewTempFile(merged);
			while (filesInRound > 0) {
				smallestIndex = 0;
				smallest = &mergeVector[0]->getRecord();
				for (int x = 0; x < filesInRound; x++) {
					aux = &mergeVector[x]->getRecord();
					if (comparator(aux, smallest)) {
						smallest = aux;
						smallestIndex = x;
					}
				}
				merged.add(*smallest);
				if (!mergeVector[smallestIndex]->next()) {
					mergeVector[smallestIndex]->close();
					filesInRound--;
					std::swap(mergeVector[smallestIndex], mergeVector[filesInRound]);
				}
			}
			merged.close();
		}
	}
	if (temps.size() > 0)
		return temps[0];
	return "";
}

template <class T>
void SecuentialMerge<T>::initNewTempFile(SecuentialFile<T>& file) {
	String tempPath = mergeName + Utils::toString(temps.size() + 1) + MERGE_EXT;
	temps.push_back(tempPath);
	file.create(tempPath);
}

template <class T>
BigInt SecuentialMerge<T>::loadMergeVector() {
	BigInt x;
	for (x = 0; x < mergeVector.size() && x < temps.size(); x++) {
		mergeVector[x]->open(temps[x]);
	}
	temps.erase(temps.begin(), temps.begin() + x);
	return x;
}

#endif /* SECUENTIALMERGE_H_ */
